package com.reyco1.medusa.remoting
{
	import com.reyco1.medusa.data.AMFCollection;
	import com.reyco1.medusa.events.ServiceEvent;
	import com.reyco1.medusa.abstract.SmartEventDispatcher;
	
	import flash.events.IOErrorEvent;
	import flash.events.NetStatusEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.NetConnection;
	import flash.net.ObjectEncoding;
	import flash.net.Responder;
	
	public class AMFService extends SmartEventDispatcher
	{
		public static var gateway:String = "";
		
		private var netConnection:NetConnection;
		private var responder:Responder;
		private var serverClass:String;
		
		private var _data:AMFCollection;
		
		public function AMFService(serverClass:String, responder:Responder = null)
		{
			this.serverClass = serverClass;
			this.responder = responder != null ? responder : new Responder(successfulResult, erroneousResult);
			
			netConnection = new NetConnection();
			netConnection.objectEncoding = ObjectEncoding.AMF3;
			netConnection.addEventListener(NetStatusEvent.NET_STATUS, handleConnectionStatus);
			netConnection.addEventListener(IOErrorEvent.IO_ERROR, handleConnectionError);
			netConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleConnectionError);
			
			if(gateway == "")
			{
				log("You must set the gateway via AMFService.gateway", "error");
			}
			else
			{
				log("connecting...");
				netConnection.connect(gateway);
			}
		}
		
		public function call(classMethod:String, arguments:Array = null):void
		{
			if(arguments != null)
			{
				var arrTemp:Array = [serverClass + "." + classMethod, responder].concat(arguments);
				netConnection.call.apply(netConnection, arrTemp);
			}
			else
			{
				netConnection.call(serverClass + "." + classMethod, responder);
			}	
			log("calling: " + serverClass + "." + classMethod);
		}
		
		/* handlers */
		
		private function handleConnectionStatus(e:NetStatusEvent):void
		{
			var objInfo:Object = e.info;
			log(objInfo.code);
			
			switch(objInfo.code) 
			{
				case "NetConnection.Connect.Failed":
					dispatchEvent(new ServiceEvent(ServiceEvent.CONNECTION_ERROR, {text:"Connection Error: " + e.info.code}));
					break;
				case "NetConnection.Call.BadVersion":
					dispatchEvent(new ServiceEvent(ServiceEvent.CONNECTION_ERROR, {text:"Connection Error: " + e.info.code}));
					log("This error usually occurs because there is something wrong with the service class.", "warning");
					break;
			}
		}
		
		private function handleConnectionError(e:*):void
		{
			var objInfo:Object = e.info;
			log(objInfo.code);
			
			dispatchEvent(new ServiceEvent(ServiceEvent.CONNECTION_ERROR, {text:"Connection Error: " + e.text}));
		}
		
		private function successfulResult(result:Object):void
		{
			var event:ServiceEvent = new ServiceEvent(ServiceEvent.RESULT);
			
			try
			{	
				event.data = new AMFCollection(result);
			}
			catch(e:Error)
			{
				event.data = result;
			}
			
			dispatchEvent(event);
		}
		
		private function erroneousResult(result:Object):void
		{
			log("Call failed!", "warning");
			var event:ServiceEvent = new ServiceEvent(ServiceEvent.FAULT);
			event.details = result;
			dispatchEvent(event);
		}
		
		/* properties */
		
		public function get data():AMFCollection
		{
			return _data;
		}
		
	}
}